package com.gonsin.gview.logic.delegate;

import com.gonsin.gview.annotations.GContext;
import com.gonsin.gview.annotations.actions.GFilename;
import com.gonsin.gview.annotations.actions.GSession;
import com.gonsin.gview.annotations.interfaces.IFileActionListener;
import com.gonsin.gview.annotations.model.FileRequest;
import com.gonsin.base.GPageRequest;
import com.gonsin.gview.logic.enums.FileUploadType;
import com.gonsin.gview.logic.exceptions.BaseGViewException;
import com.gonsin.gview.logic.services.GFileServiceImpl;
import com.gonsin.gview.logic.utils.*;
import com.google.gson.reflect.TypeToken;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.BeanUtilsBean;
import org.apache.commons.io.FileUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.NumberUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.File;
import java.lang.reflect.*;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 代理，实现确认事件
 */
@Slf4j
public class DelegateUploadActionListenerImpl implements IFileActionListener {

    /**
     * 调用方法的对象
     */
    private Object delegateService;

    private GFileServiceImpl fileService;

    /**
     * 需要调用的方法
     */
    private Method actionMethod;

    // 按钮绑定的actionName
    private String actionName;

    private GContext context;

    private static Type[] PARAMS = {
            HttpServletRequest.class,
            FileRequest.class,
            File.class,
            new TypeToken<List<File>>(){}.getType(),
            HttpSession.class,
            HttpServletResponse.class,
            String.class,
            new TypeToken<Map<String, Object>>(){}.getType(),
    };

    public DelegateUploadActionListenerImpl(GContext context, String actionName,
                                            Object delegateService, Method actionMethod) {
        // 检查方法是否有效
        this.fileService = SpringUtil.getBean(GFileServiceImpl.class);
        this.context = context;
        this.actionName = actionName;
        if (!Modifier.isPublic(actionMethod.getDeclaringClass().getModifiers())) {
            actionMethod.setAccessible(true);
        }

        this.actionMethod = actionMethod;
        this.delegateService = delegateService;

    }

    @Override
    public void onUploadFile(HttpServletRequest request, HttpServletResponse response, FileRequest fileRequest) throws Throwable {
//        int count = actionMethod.getParameterCount();
//        Object[] params = new Object[count];
        Map<String, Object> props = fileRequest.getProps();

        int count = actionMethod.getParameterCount();
        Object[] paramsArray = new Object[count];
        for(int i = 0; i < count; i++){
            Class p = actionMethod.getParameterTypes()[i];
            if(HttpSession.class.isAssignableFrom(p)){
                paramsArray[i] = request.getSession();
                continue;
            }

            if(HttpServletResponse.class.isAssignableFrom(p)){
                paramsArray[i] = response;
                continue;
            }

            if(HttpServletRequest.class.isAssignableFrom(p)){
                paramsArray[i] = request;
                continue;
            }

            GFilename gFilename = ValueUtils.getFilenameAnnotation(actionMethod, i);
            if(gFilename != null){
                if(String.class.isAssignableFrom(p)){
                    if(CollectionUtils.isEmpty(fileRequest.getFilenames())){
                        paramsArray[i] = null;
                        log.warn(String.format("【%s】中，请求的actionName=【%s】，没有文件上传", context.getViewName(), actionName));
                        continue;
                    }
                    paramsArray[i] = fileRequest.getFilenames().get(0);
                    continue;
                }
                Type listStringType = new TypeToken<List<String>>(){}.getType();
                if(TypeUtils.instanceOf(p, listStringType)){
                    if(CollectionUtils.isEmpty(fileRequest.getFilenames())){
                        paramsArray[i] = null;
                        log.warn(String.format("【%s】中，请求的actionName=【%s】，没有文件上传", context.getViewName(), actionName));
                        continue;
                    }
                    try {
                        paramsArray[i] = fileRequest.getFilenames();
                    } catch (Exception ex) {
                        log.warn(String.format("【%s】中，请求的actionName=【%s】，调用的方法【%s】，参数【%s】类型错误，必须为List<String> 或 String ",
                                context.getViewName(), actionName, actionMethod.getName(), p.getName()));
                    }
                    continue;
                }

                if(String[].class.isAssignableFrom(p)){
                    paramsArray[i] = fileRequest.getFilenames().toArray(new String[]{});
                    continue;
                }
            }

            if(File.class.isAssignableFrom(p)){
                if(CollectionUtils.isEmpty(fileRequest.getFileUrls())){
                    paramsArray[i] = null;
                    log.warn(String.format("【%s】中，请求的actionName=【%s】，没有文件上传", context.getViewName(), actionName));
                    continue;
                }
                // TODO 判断上传的是文件夹还是目录
                File file = new File(this.fileService.getPathFromUrl(fileRequest.getFileUrls().get(0)));
                paramsArray[i] = file;
                continue;
            }

            if(FileRequest.class.isAssignableFrom(p)){
                paramsArray[i] = fileRequest;
                continue;
            }

            if(File[].class.isAssignableFrom(p)){
                if(CollectionUtils.isEmpty(fileRequest.getFileUrls())){
                    paramsArray[i] = null;
                    log.warn(String.format("【%s】中，请求的actionName=【%s】，没有文件上传", context.getViewName(), actionName));
                    continue;
                }
                paramsArray[i] = fileRequest.getFileUrls().stream().map(f -> {
                    String path = this.fileService.getPathFromUrl(f);
                    File ff = new File(path);
                    return ff;
                }).toArray();
                continue;
            }

            Type listFileType = new TypeToken<List<File>>(){}.getType();
            if(TypeUtils.instanceOf(p, listFileType)){
                if(CollectionUtils.isEmpty(fileRequest.getFileUrls())){
                    paramsArray[i] = null;
                    log.warn(String.format("【%s】中，请求的actionName=【%s】，没有文件上传", context.getViewName(), actionName));
                    continue;
                }
                paramsArray[i] = fileRequest.getFileUrls().stream().map(f -> {
                    String path = this.fileService.getPathFromUrl(f);
                    File ff = new File(path);
                    return ff;
                }).collect(Collectors.toList());
                continue;
            }

            if(GPageRequest.class.isAssignableFrom(p)){
                GPageRequest pageRequest = new GPageRequest();
                pageRequest.setPage(NumberUtils.parseNumber(Objects.toString(props.get("page")), Integer.class));
                pageRequest.setCount(NumberUtils.parseNumber(Objects.toString(props.get("count")), Integer.class));
                if(props.containsKey("sort")){
                    pageRequest.setSort(Objects.toString(props.get("sort")));
                }
                paramsArray[i] = pageRequest;
                continue;
            }

            // 如果是基本数据类型
            if(ValueUtils.isWrapClass(p)){

                GSession gSession = ValueUtils.getSessionAnnotation(actionMethod, i);
                if(gSession != null){
                    String sessionName = gSession.value();
                    Object value = ValueUtils.parseValue(request.getSession().getAttribute(sessionName), p);
                    paramsArray[i] = value;
                    if(value == null){
                        log.warn("获取到session内【{}】的值为空，请检查session是否书写正确", sessionName);
                    }
                } else {
                    String name = ValueUtils.getParamName(actionMethod, i);
                    Object value = ValueUtils.parseValue(props.get(name), p);
                    paramsArray[i] = value;
                    if(value == null){
//                        log.warn("获取到【{}】的值为空，请查看是否忘记填入@GSession标注或者@GParam标注", name);
                        log.warn("获取到【{}】的值为空，请查看是否在【{}.{}】中忘记填入@GSession标注或者@GParam标注", name, delegateService.getClass().getSimpleName(), actionMethod.getName());
                    }
                }
                continue;
            }


            // 创建类转存表单
            Object searchPo = null;
            try {
                searchPo = Class.forName(p.getTypeName()).newInstance();
            } catch (Exception e) {
                log.error(e.toString(), e);
                throw new BaseGViewException(String.format("无法创建类【%s】", p.getName()));
            }
            Map<String, Object> searchPoMap = new HashMap<>();
            Field[] allFields = p.getDeclaredFields();
            for(Field field : allFields){
                try {
                    searchPoMap.put(field.getName(), ValueUtils.parseValue(props.get(field.getName()), field.getGenericType()));
                } catch (Exception ex) {
                    log.warn(ex.toString(), ex);
                    log.warn("设置参数【{}】，设置的内容为“{}“", field.getName(), props.get(field.getName()));
                }
            }
            try {
                BeanUtilsBean utils = BeanUtilsBean.getInstance();
                utils.getConvertUtils().register(new MyDateConverter(), java.util.Date.class);
                utils.getConvertUtils().register(new MyBooleanConverter(), Boolean.class);
                utils.getConvertUtils().register(new MyIntegerConverter(), Integer.class);
                utils.getConvertUtils().register(new MyLongConverter(), Long.class);
                utils.getConvertUtils().register(new MyShortConverter(), Short.class);
                utils.getConvertUtils().register(new MyByteConverter(), Byte.class);
                utils.getConvertUtils().register(new MyDoubleConverter(), Double.class);
                utils.getConvertUtils().register(new MyFloatConverter(), Float.class);
                utils.getConvertUtils().register(new MyCharConverter(), Character.class);
                utils.getConvertUtils().register(new MyListConverter(), List.class);
                utils.getConvertUtils().register(new MySetConverter(), Set.class);
                utils.populate(searchPo, searchPoMap);
                paramsArray[i] = searchPo;
                continue;
            } catch (Exception e) {
                log.error(e.toString() ,e);
                throw new BaseGViewException(String.format("无法创建类【%s】", p.getTypeName()));
            }
        }

        try {
            actionMethod.invoke(delegateService, paramsArray);
        } catch (Exception e) {
            if(e instanceof InvocationTargetException){
                throw ((InvocationTargetException) e).getTargetException();
            }
            throw e;
//            log.error(e.toString(), e);
//            throw new BaseGViewException(String.format("执行方法【%s】失败，可能是因为该方法不是public", actionMethod.getName()));
        }


//        for(int i = 0; i < count; i++){
//
////            Type p = actionMethod.getParameterTypes()[i];
//            Type p = actionMethod.getGenericParameterTypes()[i];
//            int index = ArrayUtils.indexOf(PARAMS, p);
//            Object paramObj = null;
//            switch (index) {
//                case 0: paramObj = request; break;
//                case 1: paramObj = fileRequest; break;
//                case 2: {
//                    if(CollectionUtils.isEmpty(fileRequest.getFileUrls())){
//                        paramObj = null;
//                        log.warn(String.format("【%s】中，请求的actionName=【%s】，没有文件上传", context.getViewName(), actionName));
//                        break;
//                    }
//                    File file = new File(this.fileService.getPathFromUrl(fileRequest.getFileUrls().get(0)));
//                    paramObj = file;
//                } break;
//                case 3: {
//                    if(CollectionUtils.isEmpty(fileRequest.getFileUrls())){
//                        paramObj = new ArrayList<>();
//                        log.warn(String.format("【%s】中，请求的actionName=【%s】，没有文件上传", context.getViewName(), actionName));
//                        break;
//                    }
//                    paramObj = fileRequest.getFileUrls().stream().map(m -> {
//                        File file = new File(this.fileService.getPathFromUrl(m));
//                        return file;
//                    }).collect(Collectors.toList());
//                } break;
//                case 4: {
//                    paramObj = request.getSession();
//                } break;
//                case 5: {
//                    paramObj = response;
//                } break;
//                case 6: {
//                    String key = actionMethod.getParameters()[i].getName();
//                    paramObj = props.get(key);
//                } break;
//                case 7: {
//                    paramObj = props;
//                } break;
//                default: {
//                    try {
//                        paramObj = ClassUtils.getClass(p.getTypeName()).newInstance();
//                        BeanUtils.populate(paramObj, props);
//                    } catch (Exception e) {
//                        log.error(e.toString(), e);
//                        throw new BaseGViewException(
//                                String.format("创建类【%s】失败，无法调用方法【%s】", p.getTypeName(), actionMethod.getName()));
//                    }
//                }
//            }
//            params[i] = paramObj;
//        }
//
//        try {
//            actionMethod.invoke(target, params);
//        } catch (Exception e) {
//            log.error(e.toString(), e);
////            throw new ActionRunFailedException(target, actionMethod);
//            throw new BaseGViewException(String.format("类%s的方法%s运行失败", target.getClass().getName(), actionMethod.getName()));
//        }
    }
}
